package com.reger.web.permission;

import java.lang.reflect.Method;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import com.reger.core.exception.GlobalException;
import com.reger.core.exception.GlobalExceptionStatus;
import com.reger.core.init.ControllerBefore;

@Configuration
public class PermissionAutoConfiguration implements ApplicationContextAware{

	private static final Logger log = LoggerFactory.getLogger(PermissionAutoConfiguration.class);

	private Map<String, PermissionGroup> permissionChecks;
	private ApplicationContext applicationContext;
	private final static ExpressionParser parser = new SpelExpressionParser();
	private final static GlobalException accessDenied = new GlobalException(GlobalExceptionStatus.ACCESS_DENIED, "不允许访问");

	private ParameterNameDiscoverer parameterNameDiscoverer=new DefaultParameterNameDiscoverer();
	
	public PermissionAutoConfiguration() {
		log.debug("权限控制开始初始化...");
	}
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext=applicationContext;
	}
	
	private boolean check(Method method, Object rootObject, Object[] arguments ) {
		Class<?> thisClass=AopUtils.getTargetClass(rootObject);
		Permission permissionClaz = AnnotationUtils.findAnnotation(thisClass, Permission.class);
		Permissions permissions = AnnotationUtils.findAnnotation(method, Permissions.class);
		Permission permissionMethod = AnnotationUtils.findAnnotation(method, Permission.class);
		String group = null;
		String value = null;
		String expand = null;
		if(permissionClaz!=null){
			if(!permissionClaz.group().isEmpty())
				group=permissionClaz.group();
			if(!permissionClaz.value().isEmpty())
				value=permissionClaz.value();
			if(!permissionClaz.expand().isEmpty())
				expand=permissionClaz.expand();
		}
		if(permissionChecks==null){
			permissionChecks=applicationContext.getBeansOfType(PermissionGroup.class);
		}
		Permission[] _permissions;
		if(permissions==null||permissions.value().length==0){
			if(permissionMethod==null){
				if(group==null||!permissionChecks.containsKey(group)){
					return true;
				}
				PermissionGroup permissionGroup= permissionChecks.get(group);
				return permissionGroup.check(this.buildSpEl(value, rootObject, method, arguments),expand);
			}else{
				_permissions=new Permission[]{permissionMethod};
			}
		}else{
			_permissions=permissions.value();
		}
		for (Permission permission : _permissions) {
			String _group = group;
			String _value = value;
			String _expand=expand;
			if(!permission.group().isEmpty())
				_group=permission.group();
			if(!permission.value().isEmpty())
				_value=permission.value();
			if(!permission.expand().isEmpty())
				_expand=permission.expand();
			if(!permissionChecks.containsKey(_group))
				continue;
			PermissionGroup permissionGroup= permissionChecks.get(_group);
			if(!permissionGroup.check(this.buildSpEl(_value, rootObject, method, arguments),_expand))
				return false;
		}
		return true;
	}
	private Object buildSpEl(String expressionString, Object rootObject, Method method, Object[] arguments) {
		if(expressionString==null||expressionString.isEmpty())
			return null;
		EvaluationContext context=new MethodBasedEvaluationContext(rootObject, method, arguments, parameterNameDiscoverer);
		Expression expression= parser.parseExpression(expressionString);
		return expression.getValue(context);
	}

	@Bean
	ControllerBefore controllerPermission() {
		return (point, method) -> {
			log.debug("进入权限控制方法 {}.{}", method.getDeclaringClass(),method.getName());
			if (!this.check(method,point.getTarget(),point.getArgs())) {
				throw accessDenied;
			}
		};
	}
}
